#pragma once


// Some declarations to ease parsing
#define TIS_KEYWORD(c) (c->tokenType == TOKEN_KEYWORD)
#define TIS_LSQUARE(c) (c->tokenType == TOKEN_LSQUARE)
#define TIS_RSQUARE(c) (c->tokenType == TOKEN_RSQUARE)
#define TIS_LCURLY(c) (c->tokenType == TOKEN_LCURLY)
#define TIS_RCURLY(c) (c->tokenType == TOKEN_RCURLY)
#define TIS_EQUALS(c) (c->tokenType == TOKEN_EQUALS)
#define TIS_COMMA(c)  (c->tokenType == TOKEN_COMMA)
#define TIS_LPARA(c)  (c->tokenType == TOKEN_LPARA)
#define TIS_RPARA(c)  (c->tokenType == TOKEN_RPARA)
#define TIS_SEMICOLON(c) (c->tokenType == TOKEN_SEMICOLON)

#define NODE_IS_INT(node) (node->nodeType == NODE_TOKEN && node->inner.nodeToken.tokenType == TOKEN_INT)
#define NODE_IS_CONST(node) (NODE_IS_INT(node)) 

#define TYPE_IS_INT(type) (type->primitiveType == PRIM_TYPE_INT )

namespace IcyCrystal {
	

	class Parser {
	private:
		Lexer& lexer;
		vector<SyntaxTree*> syntaxList;
		Namespace globalNamespace;
		int currentLevel;
		int currentConstCount;

		void createSystemTypes();

		Symbol* addPrimitiveToTable(Namespace* currentNamespace, Type* type, Token* keyword);
		Symbol* addConstantToTable(Namespace* currentNamespace, Type* type );
		Symbol* addStringToTable(Namespace* currentNamespace, Token& token);

		/** Does automatic conversion and promotion in TWO expressions (add, assign etc) */
		void promoteAndConvertOp(SyntaxTree* rootNode);
		/** Checks if a type is numeral */
		bool isTypeNumeral(Type* type);
		/** Promotes an int to a float using an expression parent to the current expression */
		SyntaxTree* promoteIntToFloat(SyntaxTree* node);
		SyntaxTree* promoteStringToPointer(SyntaxTree* node);

		/** Returns a new (or existing) type specifier. Returns null if tokens do not specify type */
		Type* getTypeSpecifier(Token* firstToken, Type* rec);

		SyntaxTree* createVariableDeclaration(Token* startingToken, Namespace* currentNamespace);
		SyntaxTree* getVariable( Token* startingToken, Namespace* currentNamespace, Type* preType);
		
		/** ---- Arrray Functions (Arrays.cpp) ---- */
		SyntaxTree* getArrayAccess(Namespace* currentNamespace,Type* parentType, Symbol* rootSymbol);
		Type* getArrayInitType(Namespace* currentNamespace, Type* rootType, bool isFirst);
		void computeArraySize(Namespace* currentNamespace, Type* rootType);
		void validateArrayInitializer(Namespace* currentNamespace, Type* arrayType, SyntaxTree* argumentsTree );
		bool arrayIsVLA(Type* root);

		/** --- Function-related Functions (Functions.cpp ) ---- */
		SyntaxTreeList* getArgumentList(Namespace* currentNamespace, TokenType terminatingToken);
		SymbolList* getArgumentDeclList(Namespace* currentNamespace);
		void verifyArgumentDeclList(SymbolList* argList, bool isDecl);
		Symbol* createFunctionDeclaration(string* functionName, Type* returnType, SymbolList* paramList );
		bool functionIsDefined(string& functionName);

		/** Links the next statement at the end of a specified node. */
		SyntaxTree* getNextStatement(Namespace* currentNamespace);

		/** Returns the next expression */
		SyntaxTree* getNextExpression(Namespace* currentNamespace);

		/** Returns a block */
		SyntaxTreeList* generateBlock(Namespace* newNamespace);

		/** Creates and validates and assignment expression */
		SyntaxTree* createStatAssign(Namespace* currentNamespace, SyntaxTree* symbolTree, SyntaxTree* rvalue );

		
		Type* getRootType(Type* type);


		/** Gets an expression association level */
		int getOpLevel(int tokenType);

		Type* intType;
		Type* floatType;
		Type* charType;

		void gotoNextStatement();


		unsigned int currentLine(){
			return lexer.fileReader.getCurrentLine();
		}
	
	public:
		Parser(Lexer& lexer);
		void generateSyntaxTree();

		vector<SyntaxTree*>& getSyntaxTree();
		Namespace& getGlobalNamespace();
	};

}
